<?php
/*
  Version 2.04 for MS2 and earlier

  osCommerce, Open Source E-Commerce Solutions
  http://www.oscommerce.com

  Copyright (c) 2002, 2003 Steve Fatula of Fatula Consulting
  compconsultant@yahoo.com

  v1.3.5 modified to work with zencart and made graphically acceptable by fedex identity, fedex legal and fedex third party applications.
  Tony Corbett
  merlin@realm-of-merlin.com
  
  v1.3.7 updated handling fees
  Numinix Technology
  webmaster@numinix.com
  
  v1.4.0 dimensional support
  Numinix Technology
  webmaster@numinix.com
    
  Released under the GNU General Public License
*/

class fedexexpress {
		var $code, $title, $description, $sort_order, $icon, $tax_class, $enabled, $meter, $intl;
    var $length, $width, $height, $num_pieces;
		// class constructor
		function fedexexpress() {
			global $order, $db, $template;
			$this->code = 'fedexexpress';
			$this->title = MODULE_SHIPPING_FEDEX_EXPRESS_TEXT_TITLE;
			$this->description = MODULE_SHIPPING_FEDEX_EXPRESS_TEXT_DESCRIPTION;
			$this->sort_order = MODULE_SHIPPING_FEDEX_EXPRESS_SORT_ORDER;
			$this->icon = DIR_WS_IMAGES . 'fedex-images/EXPRESS.gif';
			$this->tax_class = MODULE_SHIPPING_FEDEX_EXPRESS_TAX_CLASS;
			$this->enabled = ((MODULE_SHIPPING_FEDEX_EXPRESS_STATUS == 'True') ? true : false);
			$this->meter = MODULE_SHIPPING_FEDEX_EXPRESS_METER;

			if ( ($this->enabled == true) && ((int)MODULE_SHIPPING_FEDEX_EXPRESS_ZONE > 0) ) {
				$check_flag = false;
				$check = $db->Execute("select zone_id from " . TABLE_ZONES_TO_GEO_ZONES . " where geo_zone_id = '" . MODULE_SHIPPING_FEDEX_EXPRESS_ZONE . "' and zone_country_id = '" . $order->delivery['country']['id'] . "' order by zone_id");
				while (!$check->EOF) {
					if ($check->fields['zone_id'] < 1) {
						$check_flag = true;
						break;
					} elseif ($check->fields['zone_id'] == $order->delivery['zone_id']) {
						$check_flag = true;
						break;
					}
					$check->MoveNext();
				}

				if ($check_flag == false) {
					$this->enabled = false;
				}
		  }

      $this->domestic_types = array();
      if (MODULE_SHIPPING_FEDEX_EXPRESS_PRIORITY_OVERNIGHT == 'true') $this->domestic_types['01'] = 'FedEx Priority Overnight<sup><font size="1">&reg;</font></sup> ';
			if (MODULE_SHIPPING_FEDEX_EXPRESS_2DAY == 'true') $this->domestic_types['03'] = 'FedEx 2Day<sup><font size="1">&reg;</font></sup> ';
			if (MODULE_SHIPPING_FEDEX_EXPRESS_STANDARD_OVERNIGHT == 'true') $this->domestic_types['05'] = 'FedEx Standard Overnight<sup><font size="1">&reg;</font></sup> ';
			if (MODULE_SHIPPING_FEDEX_EXPRESS_FIRST_OVERNIGHT == 'true') $this->domestic_types['06'] = 'FedEx First Overnight<sup><font size="1">&reg;</font></sup> ';
			if (MODULE_SHIPPING_FEDEX_EXPRESS_SAVER == 'true') $this->domestic_types['20'] = 'FedEx Express Saver<sup><font size="1">&reg;</font></sup> ';
      
			$this->international_types = array();
      if (MODULE_SHIPPING_FEDEX_EXPRESS_INT_PRIORITY == 'true') $this->international_types['01'] = 'FedEx International Priority<sup><font size="1">&reg;</font></sup> ';
			if (MODULE_SHIPPING_FEDEX_EXPRESS_INT_ECONOMY == 'true') $this->international_types['03'] = 'FedEx International Economy<sup><font size="1">&reg;</font></sup> ';
			if (MODULE_SHIPPING_FEDEX_EXPRESS_INT_FIRST == 'true') $this->international_types['06']	= 'FedEx International First<sup><font size="1">&reg;</font></sup> ';
		}

		// class methods
		function quote($method = '')
		{
				global $shipping_weight, $shipping_num_boxes, $cart, $order;

				if (zen_not_null($method)) {
						$this->_setService($method);
				}

				if (MODULE_SHIPPING_FEDEX_EXPRESS_ENVELOPE == 'True') {
						if (($shipping_weight <= .5 && MODULE_SHIPPING_FEDEX_EXPRESS_WEIGHT == 'LBS') ||

										($shipping_weight <= .2 && MODULE_SHIPPING_FEDEX_EXPRESS_WEIGHT == 'KGS')) {
								$this->_setPackageType('06');
						} else {
								$this->_setPackageType('01');
						}
				} else {
						$this->_setPackageType('01');
				}

				if ($this->packageType == '01' && $shipping_weight < 1) {
						$this->_setWeight(1);
				} else {
						$this->_setWeight($shipping_weight);
				}

				$totals = $order->info['subtotal'] = $_SESSION['cart']->show_total();
        
        if (MODULE_SHIPPING_FEDEX_EXPRESS_DIMENSIONS == 'True') {
          // set the package dimensions
          $this->_getDimensions();
          // shipping_num_boxes is flawed, additional calculations required
          $shipping_num_boxes = 0;
          // calculate number of boxes needed to ship all products that are not ready to ship
          $shipping_num_boxes = ceil($this->main_package_weight/SHIPPING_MAX_WEIGHT);
          // update the total number of packages by adding ready to ship items with calculated number of boxes for items not ready to ship
          $this->num_pieces += $shipping_num_boxes;
          // give value to shipping_num_boxes for default calculations
          $shipping_num_boxes = $this->num_pieces;
        }
        $this->num_pieces = $shipping_num_boxes; 

				$this->_setInsuranceValue($totals);

				if (defined("SHIPPING_ORIGIN_COUNTRY")) {
						$countries_array = zen_get_countries(SHIPPING_ORIGIN_COUNTRY, true);

						$this->country = $countries_array['countries_iso_code_2'];
				} else {
						$this->country = STORE_ORIGIN_COUNTRY;
				}

				$fedexQuote = $this->_getQuote();

				if (is_array($fedexQuote)) {
						if (isset($fedexQuote['error'])) {
								$this->quotes = array('module' => $this->title,

										'error' => $fedexQuote['error']);
						} else {
								$this->quotes = array('id' => $this->code,

										'module' => $this->title);

								$methods = array();

								foreach ($fedexQuote as $type => $cost) {
										$skip = false;

										$this->surcharge = 0;

										if ($this->intl === false) {
												if (strlen($type) > 2 && MODULE_SHIPPING_FEDEX_EXPRESS_TRANSIT == 'True') {
														$service_descr = $this->domestic_types[substr($type, 0, 2)] . ' (' . substr($type, 2, 1) . ' days)';
												} else {
														$service_descr = $this->domestic_types[substr($type, 0, 2)];
												}

												switch (substr($type, 0, 2)) {
												case 90:

														if ($order->delivery['company'] != '') {
																$skip = true;
														}

														break;

												case 92:

														if ($this->country == "CA") {
																if ($order->delivery['company'] == '') {
																		$this->surcharge = MODULE_SHIPPING_FEDEX_EXPRESS_RESIDENTIAL;
																}
														} else {
																if ($order->delivery['company'] == '') {
																		$skip = true;
																}
														}

														break;

												default:

														if ($this->country != "CA" && substr($type, 0, 2) < "90" && $order->delivery['company'] == '') {
																$this->surcharge = MODULE_SHIPPING_FEDEX_EXPRESS_RESIDENTIAL;
														}

														break;
												}
										} else {
												if (strlen($type) > 2 && MODULE_SHIPPING_FEDEX_EXPRESS_TRANSIT == 'True') {
														$service_descr = $this->international_types[substr($type, 0, 2)] . ' (' . substr($type, 2, 1) . ' days)';
												} else {
														$service_descr = $this->international_types[substr($type, 0, 2)];
												}
										}

										if ($method) {
												if (substr($type, 0, 2) != $method) $skip = true;
										}
										
										if ($order->delivery['country']['id'] == STORE_COUNTRY) {
											$fixed_surcharge = MODULE_SHIPPING_FEDEX_EXPRESS_SURCHARGE_DOM;
											$variable_surcharge = MODULE_SHIPPING_FEDEX_EXPRESS_SURCHARGE_PERCENT_DOM;
										} else {
											$fixed_surcharge = MODULE_SHIPPING_FEDEX_EXPRESS_SURCHARGE_INT;
											$variable_surcharge = MODULE_SHIPPING_FEDEX_EXPRESS_SURCHARGE_PERCENT_INT;
										}
											
										if (!$skip) {
                      // unlike FedEx Ground, there appears to be a bug with the number of packages, and thus the rate returned needs to be multiplied by the number of packages
												$methods[] = array('id' => substr($type, 0, 2),

														'title' => $service_descr,

														'cost' => SHIPPING_HANDLING + $fixed_surcharge + $this->surcharge + ($cost * $this->num_pieces) + (($cost * $this->num_pieces) * $variable_surcharge / 100));
										}
								}

								$this->quotes['methods'] = $methods;

								if ($this->tax_class > 0) {
										$this->quotes['tax'] = zen_get_tax_rate($this->tax_class, $order->delivery['country']['id'], $order->delivery['zone_id']);
								}
						}
				} else {
						$this->quotes = array('module' => $this->title,

								'error' => 'An error occured with the fedex shipping calculations.<br />Fedex may not deliver to your country, or your postal code may be wrong.');
				}

				if (zen_not_null($this->icon)) $this->quotes['icon'] = zen_image($this->icon, $this->title);

				return $this->quotes;
		}

		function check()
		{
				global $db;

				if (!isset($this->_check)) {
						$check_query = $db->Execute("select configuration_value from " . TABLE_CONFIGURATION . " where configuration_key = 'MODULE_SHIPPING_FEDEX_EXPRESS_STATUS'");

						$this->_check = $check_query->RecordCount();
				}

				return $this->_check;
		}

		function install()
		{
				global $db;

				$db->Execute("insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, set_function, date_added) values ('Enable Fedex Shipping', 'MODULE_SHIPPING_FEDEX_EXPRESS_STATUS', 'True', 'Do you want to offer Fedex shipping?', '6', '1', 'zen_cfg_select_option(array(\'True\', \'False\'), ', now())");
				$db->Execute("insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, date_added) values ('Your Fedex Account Number', 'MODULE_SHIPPING_FEDEX_EXPRESS_ACCOUNT', 'NONE', 'Enter the fedex Account Number assigned to you, required', '6', '2', now())");
				$db->Execute("insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, date_added) values ('Your Fedex Meter ID', 'MODULE_SHIPPING_FEDEX_EXPRESS_METER', 'NONE', 'Enter the Fedex MeterID assigned to you, set to NONE to obtain a new meter number', '6', '3', now())");
				$db->Execute("insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, date_added) values ('cURL Path', 'MODULE_SHIPPING_FEDEX_EXPRESS_CURL', 'NONE', 'Enter the path to the cURL program, normally, leave this set to NONE to execute cURL using PHP', '6', '4', now())");
				// added for proxy
				$db->Execute("insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, date_added) values ('cURL Proxy', 'MODULE_SHIPPING_FEDEX_EXPRESS_CURL_PROXY', 'NONE', 'Enter the Complete Url includeing http:// to your proxy server. If not needed leave set to NONE.', '6', '5', now())");
				// end of proxy
				$db->Execute("insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, set_function, date_added) values ('Debug Mode', 'MODULE_SHIPPING_FEDEX_EXPRESS_DEBUG', 'False', 'Turn on Debug', '6', '6', 'zen_cfg_select_option(array(\'True\', \'False\'), ', now())");
		    $db->Execute("insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, set_function, date_added) values ('Enable Dimensions?', 'MODULE_SHIPPING_FEDEX_EXPRESS_DIMENSIONS', 'False', 'Do you want to add dimensional support?', '6', '7', 'zen_cfg_select_option(array(\'True\', \'False\'), ', now())");
        $db->Execute("insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, set_function, date_added) values ('Weight Units', 'MODULE_SHIPPING_FEDEX_EXPRESS_WEIGHT', 'LBS', 'Weight Units:', '6', '7', 'zen_cfg_select_option(array(\'LBS\', \'KGS\'), ', now())");
				$db->Execute("insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, date_added) values ('First line of street address', 'MODULE_SHIPPING_FEDEX_EXPRESS_ADDRESS_1', 'NONE', 'Enter the first line of your ship-from street address, required', '6', '8', now())");
				$db->Execute("insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, date_added) values ('Second line of street address', 'MODULE_SHIPPING_FEDEX_EXPRESS_ADDRESS_2', 'NONE', 'Enter the second line of your ship-from street address, leave set to NONE if you do not need to specify a second line', '6', '9', now())");
				$db->Execute("insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, date_added) values ('City name', 'MODULE_SHIPPING_FEDEX_EXPRESS_CITY', 'NONE', 'Enter the city name for the ship-from street address, required', '6', '10', now())");
				$db->Execute("insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, date_added) values ('State or Province name', 'MODULE_SHIPPING_FEDEX_EXPRESS_STATE', 'NONE', 'Enter the 2 letter state or province name for the ship-from street address, required for Canada and US', '6', '11', now())");
				$db->Execute("insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, date_added) values ('Postal code', 'MODULE_SHIPPING_FEDEX_EXPRESS_POSTAL', 'NONE', 'Enter the postal code for the ship-from street address, required', '6', '12', now())");
				$db->Execute("insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, date_added) values ('Phone number', 'MODULE_SHIPPING_FEDEX_EXPRESS_PHONE', 'NONE', 'Enter a contact phone number for your company, required', '6', '13', now())");
				$db->Execute("insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, set_function, date_added) values ('Which server to use', 'MODULE_SHIPPING_FEDEX_EXPRESS_SERVER', 'production', 'You must have an account with Fedex', '6', '14', 'zen_cfg_select_option(array(\'test\', \'production\'), ', now())");
				$db->Execute("insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, date_added) values ('Drop off type', 'MODULE_SHIPPING_FEDEX_EXPRESS_DROPOFF', '1', 'Dropoff type (1 = Regular pickup, 2 = request courier, 3 = drop box, 4 = drop at BSC, 5 = drop at station)?', '6', '15', now())");
				
				$db->Execute("insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, date_added) values ('Fedex Domestic fixed surcharge?', 'MODULE_SHIPPING_FEDEX_EXPRESS_SURCHARGE_DOM', '0', 'Fixed surcharge amount to add to domestic shipping charge?', '6', '16', now())");
				$db->Execute("insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, date_added) values ('Fedex Domestic variable surcharge?', 'MODULE_SHIPPING_FEDEX_EXPRESS_SURCHARGE_PERCENT_DOM', '0', 'Percentage surcharge amount to add to domestic shipping charge?', '6', '16', now())");
				$db->Execute("insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, date_added) values ('Fedex International fixed surcharge?', 'MODULE_SHIPPING_FEDEX_EXPRESS_SURCHARGE_INT', '0', 'Fixed surcharge amount to add to international shipping charge?', '6', '17', now())");
				$db->Execute("insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, date_added) values ('Fedex International variable surcharge?', 'MODULE_SHIPPING_FEDEX_EXPRESS_SURCHARGE_PERCENT_INT', '0', 'Percentage surcharge amount to add to international shipping charge?', '6', '17', now())");

				$db->Execute("insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, set_function, date_added) values ('Show List Rates?', 'MODULE_SHIPPING_FEDEX_EXPRESS_LIST_RATES', 'False', 'This needs to be left set to false', '6', '18', 'zen_cfg_select_option(array(\'True\', \'False\'), ', now())");
				$db->Execute("insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, date_added) values ('Residential surcharge?', 'MODULE_SHIPPING_FEDEX_EXPRESS_RESIDENTIAL', '0', 'Residential Surcharge (in addition to other surcharge) for Express packages within US, or ground packages within Canada?', '6', '19', now())");
				$db->Execute("insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, date_added) values ('Insurance?', 'MODULE_SHIPPING_FEDEX_EXPRESS_INSURE', 'NONE', 'Insure packages over what dollar amount?', '6', '20', now())");
				$db->Execute("insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, set_function, date_added) values ('Enable Envelope Rates?', 'MODULE_SHIPPING_FEDEX_EXPRESS_ENVELOPE', 'False', 'Do you want to offer Fedex Envelope rates? All items under 1/2 LB (.23KG) will quote using the envelope rate if True.', '6', '21', 'zen_cfg_select_option(array(\'True\', \'False\'), ', now())");
				$db->Execute("insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, set_function, date_added) values ('Sort rates: ', 'MODULE_SHIPPING_FEDEX_EXPRESS_WEIGHT_SORT', 'High to Low', 'Sort rates top to bottom: ', '6', '22', 'zen_cfg_select_option(array(\'High to Low\', \'Low to High\'), ', now())");
				$db->Execute("insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, date_added) values ('Timeout in Seconds', 'MODULE_SHIPPING_FEDEX_EXPRESS_TIMEOUT', 'NONE', 'Enter the maximum time in seconds you would wait for a rate request from Fedex? Leave NONE for default timeout.', '6', '23', now())");
				$db->Execute("insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, use_function, set_function, date_added) values ('Tax Class', 'MODULE_SHIPPING_FEDEX_EXPRESS_TAX_CLASS', '0', 'Use the following tax class on the shipping fee.', '6', '24', 'zen_get_tax_class_title', 'zen_cfg_pull_down_tax_classes(', now())");
        $db->Execute("insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, use_function, set_function, date_added) values ('Shipping Zone', 'MODULE_SHIPPING_FEDEX_EXPRESS_ZONE', '0', 'If a zone is selected, only enable this shipping method for that zone.', '6', '0', 'zen_get_zone_class_title', 'zen_cfg_pull_down_zone_classes(', now())");
				$db->Execute("insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, date_added) values ('Currency Code', 'MODULE_SHIPPING_FEDEX_EXPRESS_CURRENCY', 'USD', 'Enter 3 digit currency code (default = USD)', '6', '25', now())");
        
        $db->Execute("insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, set_function, date_added) values ('Priority Overnight: ', 'MODULE_SHIPPING_FEDEX_EXPRESS_PRIORITY_OVERNIGHT', 'true', 'Offer Priority Overnight?', '6', '30', 'zen_cfg_select_option(array(\'true\', \'false\'), ', now())");
        $db->Execute("insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, set_function, date_added) values ('2Day: ', 'MODULE_SHIPPING_FEDEX_EXPRESS_2DAY', 'true', 'Offer 2Day?', '6', '31', 'zen_cfg_select_option(array(\'true\', \'false\'), ', now())");
        $db->Execute("insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, set_function, date_added) values ('Standard Overnight: ', 'MODULE_SHIPPING_FEDEX_EXPRESS_STANDARD_OVERNIGHT', 'true', 'Offer Standard Overnight?', '6', '32', 'zen_cfg_select_option(array(\'true\', \'false\'), ', now())");
        $db->Execute("insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, set_function, date_added) values ('First Overnight: ', 'MODULE_SHIPPING_FEDEX_EXPRESS_FIRST_OVERNIGHT', 'true', 'Offer First Overnight?', '6', '33', 'zen_cfg_select_option(array(\'true\', \'false\'), ', now())");
        $db->Execute("insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, set_function, date_added) values ('Express Saver: ', 'MODULE_SHIPPING_FEDEX_EXPRESS_SAVER', 'true', 'Offer Express Saver?', '6', '34', 'zen_cfg_select_option(array(\'true\', \'false\'), ', now())");
        $db->Execute("insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, set_function, date_added) values ('International Priority: ', 'MODULE_SHIPPING_FEDEX_EXPRESS_INT_PRIORITY', 'true', 'Offer International Priority?', '6', '35', 'zen_cfg_select_option(array(\'true\', \'false\'), ', now())");
        $db->Execute("insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, set_function, date_added) values ('International Economy: ', 'MODULE_SHIPPING_FEDEX_EXPRESS_INT_ECONOMY', 'true', 'Offer International Economy?', '6', '36', 'zen_cfg_select_option(array(\'true\', \'false\'), ', now())");
        $db->Execute("insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, set_function, date_added) values ('International First: ', 'MODULE_SHIPPING_FEDEX_EXPRESS_INT_FIRST', 'true', 'Offer International First?', '6', '37', 'zen_cfg_select_option(array(\'true\', \'false\'), ', now())");

        $db->Execute("insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, date_added) values ('Sort Order', 'MODULE_SHIPPING_FEDEX_EXPRESS_SORT_ORDER', '0', 'Sort order of display.', '6', '99', now())");
		}

		function remove()
		{
				global $db;

				$db->Execute("delete from " . TABLE_CONFIGURATION . " where configuration_key in ('" . implode("', '", $this->keys()) . "')");
		}

		function keys()
		{
				return array('MODULE_SHIPPING_FEDEX_EXPRESS_STATUS', 'MODULE_SHIPPING_FEDEX_EXPRESS_ACCOUNT', 'MODULE_SHIPPING_FEDEX_EXPRESS_METER', 'MODULE_SHIPPING_FEDEX_EXPRESS_CURL', 'MODULE_SHIPPING_FEDEX_EXPRESS_CURL_PROXY', 'MODULE_SHIPPING_FEDEX_EXPRESS_DEBUG', 'MODULE_SHIPPING_FEDEX_EXPRESS_WEIGHT', 'MODULE_SHIPPING_FEDEX_EXPRESS_SERVER', 'MODULE_SHIPPING_FEDEX_EXPRESS_ADDRESS_1', 'MODULE_SHIPPING_FEDEX_EXPRESS_ADDRESS_2', 'MODULE_SHIPPING_FEDEX_EXPRESS_CITY', 'MODULE_SHIPPING_FEDEX_EXPRESS_STATE', 'MODULE_SHIPPING_FEDEX_EXPRESS_POSTAL', 'MODULE_SHIPPING_FEDEX_EXPRESS_PHONE', 'MODULE_SHIPPING_FEDEX_EXPRESS_DROPOFF', 'MODULE_SHIPPING_FEDEX_EXPRESS_SURCHARGE_DOM', 'MODULE_SHIPPING_FEDEX_EXPRESS_SURCHARGE_PERCENT_DOM', 'MODULE_SHIPPING_FEDEX_EXPRESS_SURCHARGE_INT', 'MODULE_SHIPPING_FEDEX_EXPRESS_SURCHARGE_PERCENT_INT', 'MODULE_SHIPPING_FEDEX_EXPRESS_LIST_RATES', 'MODULE_SHIPPING_FEDEX_EXPRESS_INSURE', 'MODULE_SHIPPING_FEDEX_EXPRESS_RESIDENTIAL', 'MODULE_SHIPPING_FEDEX_EXPRESS_ENVELOPE', 'MODULE_SHIPPING_FEDEX_EXPRESS_WEIGHT_SORT', 'MODULE_SHIPPING_FEDEX_EXPRESS_TIMEOUT', 'MODULE_SHIPPING_FEDEX_EXPRESS_TAX_CLASS', 'MODULE_SHIPPING_FEDEX_EXPRESS_ZONE', 'MODULE_SHIPPING_FEDEX_EXPRESS_CURRENCY', 'MODULE_SHIPPING_FEDEX_EXPRESS_DIMENSIONS', 'MODULE_SHIPPING_FEDEX_EXPRESS_PRIORITY_OVERNIGHT', 'MODULE_SHIPPING_FEDEX_EXPRESS_2DAY', 'MODULE_SHIPPING_FEDEX_EXPRESS_STANDARD_OVERNIGHT', 'MODULE_SHIPPING_FEDEX_EXPRESS_FIRST_OVERNIGHT', 'MODULE_SHIPPING_FEDEX_EXPRESS_SAVER', 'MODULE_SHIPPING_FEDEX_EXPRESS_INT_PRIORITY', 'MODULE_SHIPPING_FEDEX_EXPRESS_INT_ECONOMY', 'MODULE_SHIPPING_FEDEX_EXPRESS_INT_FIRST', 'MODULE_SHIPPING_FEDEX_EXPRESS_SORT_ORDER');
		}

		function _setService($service)
		{
				$this->service = $service;
		}

		function _setWeight($pounds)
		{
				$this->pounds = sprintf("%01.1f", $pounds);
		}

		function _setPackageType($type)
		{
				$this->packageType = $type;
		}

		function _setInsuranceValue($order_amount)
		{
				if ($order_amount > MODULE_SHIPPING_FEDEX_EXPRESS_INSURE) {
						$this->insurance = sprintf("%01.2f", $order_amount);
				} else {
						$this->insurance = 0;
				}
		}

		function _AccessFedex($data)
		{
				if (MODULE_SHIPPING_FEDEX_EXPRESS_SERVER == 'production') {
						$this->server = 'gateway.fedex.com/GatewayDC';
				} else {
						$this->server = 'gatewaybeta.fedex.com/GatewayDC';
				}

				if (MODULE_SHIPPING_FEDEX_EXPRESS_CURL == "NONE") {
						$ch = curl_init();
						// added for proxy
						if (MODULE_SHIPPING_FEDEX_EXPRESS_CURL_PROXY != 'NONE') {
								curl_setopt ($ch, CURLOPT_HTTPPROXYTUNNEL, true);
								curl_setopt ($ch, CURLOPT_PROXYTYPE, CURLPROXY_HTTP);
								curl_setopt ($ch, CURLOPT_PROXY, MODULE_SHIPPING_FEDEX_EXPRESS_CURL_PROXY);
						}
						// end proxy settings
						curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);

						curl_setopt($ch, CURLOPT_URL, 'https://' . $this->server);

						if (MODULE_SHIPPING_FEDEX_EXPRESS_TIMEOUT != 'NONE') curl_setopt($ch, CURLOPT_TIMEOUT, MODULE_SHIPPING_FEDEX_EXPRESS_TIMEOUT);

						curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);

						curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);

						curl_setopt($ch, CURLOPT_HTTPHEADER, array("Referer: " . STORE_NAME,

										"Host: " . $this->server,

										"Accept: image/gif,image/jpeg,image/pjpeg,text/plain,text/html,*/*",

										"Pragma:",

										"Content-Type:image/gif"));

						curl_setopt($ch, CURLOPT_POSTFIELDS, $data);

						$reply = curl_exec($ch);

						curl_close ($ch);
				} else {
						$this->command_line = MODULE_SHIPPING_FEDEX_EXPRESS_CURL . " " . (MODULE_SHIPPING_FEDEX_EXPRESS_TIMEOUT == 'NONE' ? '' : '-m ' . MODULE_SHIPPING_FEDEX_EXPRESS_TIMEOUT) . " -s -e '" . STORE_NAME . "' --url https://" . $this->server . " -H 'Host: " . $this->server . "' -H 'Accept: image/gif,image/jpeg,image/pjpeg,text/plain,text/html,*/*' -H 'Pragma:' -H 'Content-Type:image/gif' -d '" . $data . "' 'https://" . $this->server . "'";

						exec($this->command_line, $this->reply);

						$reply = $this->reply[0];
				}

				return $reply;
		}

		function _getMeter()
		{
				$data = '0,"211"'; // Transaction Code, required
				$data .= '10,"' . MODULE_SHIPPING_FEDEX_EXPRESS_ACCOUNT . '"'; // Sender Fedex account number
				$data .= '4003,"' . STORE_OWNER . '"'; // Subscriber contact name
				$data .= '4007,"' . STORE_NAME . '"'; // Subscriber company name
				$data .= '4008,"' . MODULE_SHIPPING_FEDEX_EXPRESS_ADDRESS_1 . '"'; // Subscriber Address line 1
				if (MODULE_SHIPPING_FEDEX_EXPRESS_ADDRESS_2 != 'NONE') {
						$data .= '4009,"' . MODULE_SHIPPING_FEDEX_EXPRESS_ADDRESS_2 . '"'; // Subscriber Address Line 2
				}

				$data .= '4011,"' . MODULE_SHIPPING_FEDEX_EXPRESS_CITY . '"'; // Subscriber City Name
				if (MODULE_SHIPPING_FEDEX_EXPRESS_STATE != 'NONE') {
						$data .= '4012,"' . MODULE_SHIPPING_FEDEX_EXPRESS_STATE . '"'; // Subscriber State code
				}

				$data .= '4013,"' . MODULE_SHIPPING_FEDEX_EXPRESS_POSTAL . '"'; // Subscriber Postal Code
				$data .= '4014,"' . $this->country . '"'; // Subscriber Country Code
				$data .= '4015,"' . MODULE_SHIPPING_FEDEX_EXPRESS_PHONE . '"'; // Subscriber phone number
				$data .= '99,""'; // End of Record, required
				if (MODULE_SHIPPING_FEDEX_EXPRESS_DEBUG == 'True') echo "Data sent to Fedex for Meter: " . $data . "<br />";

				$fedexData = $this->_AccessFedex($data);

				if (MODULE_SHIPPING_FEDEX_EXPRESS_DEBUG == 'True') echo "Data returned from Fedex for Meter: " . $fedexData . "<br />";

				$meterStart = strpos($fedexData, '"498,"');

				if ($meterStart === false) {
						if (strlen($fedexData) == 0) {
								$this->error_message = 'No response to CURL from Fedex server, check CURL availability, or maybe timeout was set too low, or maybe the Fedex site is down';
						} else {
								$fedexData = $this->_ParseFedex($fedexData);

								$this->error_message = 'No meter number was obtained, check configuration. Error ' . $fedexData['2'] . ' : ' . $fedexData['3'];
						}

						return false;
				}

				$meterStart += 6;

				$meterEnd = strpos($fedexData, '"', $meterStart);

				$this->meter = substr($fedexData, $meterStart, $meterEnd - $meterStart);

				$meter_sql = "UPDATE " . TABLE_CONFIGURATION . " SET configuration_value=\"" . $this->meter . "\" where configuration_key=\"MODULE_SHIPPING_FEDEX_EXPRESS_METER\"";

				global $db;

				$db->Execute($meter_sql);

				return true;
		}

		function _ParseFedex($data)
		{
				$current = 0;

				$length = strlen($data);

				$resultArray = array();

				while ($current < $length) {
						$endpos = strpos($data, ',', $current);

						if ($endpos === false) {
								break;
						}

						$index = substr($data, $current, $endpos - $current);

						$current = $endpos + 2;

						$endpos = strpos($data, '"', $current);

						$resultArray[$index] = substr($data, $current, $endpos - $current);

						$current = $endpos + 1;
				}

				return $resultArray;
		}
    
    // creates the total length, width, and height
    function _getDimensions() {
      global $order, $db;
      if (MODULE_SHIPPING_FEDEX_EXPRESS_DIMENSIONS == 'True') {
        $this->main_package_weight = $this->num_pieces = $this->length = $this->width = $this->height = 0;
        $products = $_SESSION['cart']->get_products();
        foreach($products as $product){
          $sql = $db->Execute("SELECT products_length, products_width, products_height, products_ready_to_ship, product_is_always_free_shipping, products_weight FROM " . TABLE_PRODUCTS . " WHERE products_id = " . (int)$product['id'] . " LIMIT 1");
          if ($sql->fields['product_is_always_free_shipping'] != 1) {
            if ($sql->fields['products_ready_to_ship'] == '1') {
              $this->num_pieces += $product['quantity'];
            } else {             
              // add up weight of products not shipped on their own
              $this->main_package_weight += $sql->fields['products_weight'];            
            }
            if ($sql->fields['products_length'] || $sql->fields['products_width'] || $sql->fields['products_height']) {
              // check if max length
              if ($sql->fields['products_length'] > $this->length) {
                $this->length = $sql->fields['products_length'];
              }
              // check if max width
              if ($sql->fields['products_width'] > $this->width) {
                $this->width = $sql->fields['products_width'];
              }
              // check if max height
              if ($sql->fields['products_height'] > $this->height) {
                $this->height = $sql->fields['products_height'];
              }
            } else {
              $this->length = $this->width = $this->height = 1;
              // kill the loop
              break;
            }
          }
        }
      }
    }    

		function _getQuote()
		{
				global $order, $customer_id, $sendto;

				if (MODULE_SHIPPING_FEDEX_EXPRESS_ACCOUNT == "NONE" || strlen(MODULE_SHIPPING_FEDEX_EXPRESS_ACCOUNT) == 0) {
						return array('error' => 'You forgot to set up your Fedex account number, this can be set up in Admin -> Modules -> Shipping');
				}

				if (MODULE_SHIPPING_FEDEX_EXPRESS_ADDRESS_1 == "NONE" || strlen(MODULE_SHIPPING_FEDEX_EXPRESS_ADDRESS_1) == 0) {
						return array('error' => 'You forgot to set up your ship-from street address line 1, this can be set up in Admin -> Modules -> Shipping');
				}

				if (MODULE_SHIPPING_FEDEX_EXPRESS_CITY == "NONE" || strlen(MODULE_SHIPPING_FEDEX_EXPRESS_CITY) == 0) {
						return array('error' => 'You forgot to set up your ship-from City, this can be set up in Admin -> Modules -> Shipping');
				}

				if (MODULE_SHIPPING_FEDEX_EXPRESS_POSTAL == "NONE" || strlen(MODULE_SHIPPING_FEDEX_EXPRESS_POSTAL) == 0) {
						return array('error' => 'You forgot to set up your ship-from postal code, this can be set up in Admin -> Modules -> Shipping');
				}

				if (MODULE_SHIPPING_FEDEX_EXPRESS_PHONE == "NONE" || strlen(MODULE_SHIPPING_FEDEX_EXPRESS_PHONE) == 0) {
						return array('error' => 'You forgot to set up your ship-from phone number, this can be set up in Admin -> Modules -> Shipping');
				}

				if (MODULE_SHIPPING_FEDEX_EXPRESS_METER == "NONE") {
						if ($this->_getMeter() === false) return array('error' => $this->error_message);
				}

				$data = '0,"25"'; // TransactionCode
				$data .= '10,"' . MODULE_SHIPPING_FEDEX_EXPRESS_ACCOUNT . '"'; // Sender fedex account number
				$data .= '498,"' . $this->meter . '"'; // Meter number
				$data .= '8,"' . MODULE_SHIPPING_FEDEX_EXPRESS_STATE . '"'; // Sender state code
				$orig_zip = str_replace(array(' ', '-'), '', MODULE_SHIPPING_FEDEX_EXPRESS_POSTAL);

				$data .= '9,"' . $orig_zip . '"'; // Origin postal code
				$data .= '117,"' . $this->country . '"'; // Origin country
				$dest_zip = str_replace(array(' ', '-'), '', $order->delivery['postcode']);

				$data .= '17,"' . $dest_zip . '"'; // Recipient zip code
				if ($order->delivery['country']['iso_code_2'] == "US" || $order->delivery['country']['iso_code_2'] == "CA" || $order->delivery['country']['iso_code_2'] == "PR") {
						$state .= zen_get_zone_code($order->delivery['country']['id'], $order->delivery['zone_id'], ''); // Recipient state
						if ($state == "QC") $state = "PQ";

						$data .= '16,"' . $state . '"'; // Recipient state
				}

				$data .= '50,"' . $order->delivery['country']['iso_code_2'] . '"'; // Recipient country
				$data .= '75,"' . MODULE_SHIPPING_FEDEX_EXPRESS_WEIGHT . '"'; // Weight units
				if (MODULE_SHIPPING_FEDEX_EXPRESS_WEIGHT == "KGS") {
						$data .= '1116,"C"'; // Dimension units
				} else {
						$data .= '1116,"I"'; // Dimension units
				}

				$data .= '1401,"' . $this->pounds . '"'; // Total weight
        // set the package dimensions
        if (MODULE_SHIPPING_FEDEX_EXPRESS_DIMENSIONS == 'True') {  
          $data .= '57,"' . ceil($this->height) . '"'; // Total Height
          $data .= '58,"' . ceil($this->width) . '"'; // Total Width
          $data .= '59,"' . ceil($this->length) . '"'; // Total Length
        }
        $data .= '116,"' . $this->num_pieces . '"'; // Total number of pieces 
				
        $data .= '1529,"1"'; // Quote discounted rates
				if ($this->insurance > 0) {
					$data .= '1415,"' . $this->insurance . '"'; // Insurance value
					$data .= '68,"' . MODULE_SHIPPING_FEDEX_EXPRESS_CURRENCY . '"'; // Insurance value currency
				}

				if ($order->delivery['company'] == '' && MODULE_SHIPPING_FEDEX_EXPRESS_RESIDENTIAL == 0) {
					$data .= '440,"Y"'; // Residential address
				} else {
					$data .= '440,"N"'; // Business address, use if adding a residential surcharge
				}

				$data .= '1273,"' . $this->packageType . '"'; // Package type
				$data .= '1333,"' . MODULE_SHIPPING_FEDEX_EXPRESS_DROPOFF . '"'; // Drop of drop off or pickup
				$data .= '99,""'; // End of record
				if (MODULE_SHIPPING_FEDEX_EXPRESS_DEBUG == 'True') echo "Data sent to Fedex for Rating: " . $data . "<br />";

				$fedexData = $this->_AccessFedex($data);

				if (MODULE_SHIPPING_FEDEX_EXPRESS_DEBUG == 'True') echo "Data returned from Fedex for Rating: " . $fedexData . "<br />";

				if (strlen($fedexData) == 0) {
						$this->error_message = 'No data returned from Fedex, perhaps the Fedex site is down';

						return array('error' => $this->error_message);
				}

				$fedexData = $this->_ParseFedex($fedexData);

				$i = 1;

				if ($this->country == $order->delivery['country']['iso_code_2']) {
						$this->intl = false;
				} else {
						$this->intl = true;
				}

				$rates = null;

				while (isset($fedexData['1274-' . $i])) {
						if ($this->intl) {
								if (isset($this->international_types[$fedexData['1274-' . $i]])) {
										if (defined(MODULE_SHIPPING_FEDEX_EXPRESS_LIST_RATES) == false ||
														MODULE_SHIPPING_FEDEX_EXPRESS_LIST_RATES == 'False') {
												if (isset($fedexData['3058-' . $i])) {
														$rates[$fedexData['1274-' . $i] . $fedexData['3058-' . $i]] = $fedexData['1419-' . $i];
												} else {
														$rates[$fedexData['1274-' . $i]] = $fedexData['1419-' . $i];
												}
										} else {
												if (isset($fedexData['3058-' . $i])) {
														$rates[$fedexData['1274-' . $i] . $fedexData['3058-' . $i]] = $fedexData['1528-' . $i];
												} else {
														$rates[$fedexData['1274-' . $i]] = $fedexData['1528-' . $i];
												}
										}
								}
						} else {
								if (isset($this->domestic_types[$fedexData['1274-' . $i]])) {
										if (MODULE_SHIPPING_FEDEX_EXPRESS_LIST_RATES == 'False') {
												if (isset($fedexData['3058-' . $i])) {
														$rates[$fedexData['1274-' . $i] . $fedexData['3058-' . $i]] = $fedexData['1419-' . $i];
												} else {
														$rates[$fedexData['1274-' . $i]] = $fedexData['1419-' . $i];
												}
										} else {
												if (isset($fedexData['3058-' . $i])) {
														$rates[$fedexData['1274-' . $i] . $fedexData['3058-' . $i]] = $fedexData['1528-' . $i];
												} else {
														$rates[$fedexData['1274-' . $i]] = $fedexData['1528-' . $i];
												}
										}
								}
						}

						$i++;
				}

				if (is_array($rates)) {
						if (MODULE_SHIPPING_FEDEX_EXPRESS_WEIGHT_SORT == 'Low to High') {
								asort($rates);
						} else {
								arsort($rates);
						}
				} else {
						$this->error_message = 'No Rates Returned, ' . $fedexData['2'] . ' : ' . $fedexData['3'];

						return array('error' => $this->error_message);
				}

				return ((sizeof($rates) > 0) ? $rates : false);
		}
}

?>